package com.example.app.exponential_smoothing_algorithm.triple;

import lombok.extern.slf4j.Slf4j;

import java.text.DecimalFormat;
import java.util.*;

/**
 * @author wangzhuangzhuang
 * @DESC:
 * @date 2022-02-23 15:19
 */
@Slf4j
public class RefactoredExponentialSmoothing {


	public static void main(String[] args) {
		TripleSmoothingEntity tripleEntity = new TripleSmoothingEntity();
//		List<Double> data = Arrays.asList(225.2, 249.9, 263.2, 293.6, 318.9, 356.7, 363.3, 424.2, 466.5, 582.0, 750.0, 777.0);

		List<Double> data = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); // 7.334374
//		List<Double> data = Arrays.asList(1.0, 4.0, 7.0, 10.0, 13.0); // 17.124
//		List<Double> data = Arrays.asList(1.0, 4.0, 7.0, 10.0, 4.0); // 3.612500000000008
		// 如果发展变化趋势比较平稳的观察值，初始值可以直接用第一个数据
		// 如果变化趋势起伏较大，则要用平均值做初始值。（减少初始值对平滑值的影响）
		double originalParam = getAvgFromRealData(data);
		log.info("originalParam: {}", originalParam);
		int predict_time = 1;
		setTripleEntity(tripleEntity, data, originalParam, predict_time);
		// 平滑系数
		Double smoothingFactor = 0.5;
		double resultOfPrediction = tripleExponentialSmoothingMethod(tripleEntity, smoothingFactor);
		log.info("预测结果: {}", resultOfPrediction);
	}

	private static void setTripleEntity(TripleSmoothingEntity tripleEntity, List<Double> data, double originalParam, int predict_time) {
		tripleEntity.setRealDataList(data);
		tripleEntity.setLastSinglePredictParam(originalParam);
		tripleEntity.setLastSecondPredictParam(originalParam);
		tripleEntity.setLastTriplePredictParam(originalParam);
		tripleEntity.setPredictTime(predict_time);
	}

	// 得到数组平均值
	private static double getAvgFromRealData(List<Double> real_data_list) {
		double sum = 0.0;
		for (int i = 0; i < real_data_list.size(); i++) {
			sum += real_data_list.get(i);
		}
		return sum / real_data_list.size();
	}


	public static double tripleExponentialSmoothingMethod(TripleSmoothingEntity tripleSmoothingEntity, Double smoothingFactor) {
		// （1）获取实际观察值列表和最后一次的预测值
		List<Double> realParamList = tripleSmoothingEntity.getRealDataList();
		Double lastSinglePredictParam = tripleSmoothingEntity.getLastSinglePredictParam();
		Double lastSecondPredictParam = tripleSmoothingEntity.getLastSecondPredictParam();
		Double lastTriplePredictParam = tripleSmoothingEntity.getLastTriplePredictParam();
		// 获取总期数计算均方误差
		int totalTimes = realParamList.size();
		// 用于复位的
		Double resizeSecondPredict = tripleSmoothingEntity.getLastSecondPredictParam();
		Double resizeTriplePredict = tripleSmoothingEntity.getLastTriplePredictParam();
		int predictTime = tripleSmoothingEntity.getPredictTime();
		// 定义结果集合类
		List<Double> singleGapList = new ArrayList<>();
		List<Double> secondGapList = new ArrayList<>();
		List<Double> tripleGapList = new ArrayList<>();
		Map<Double, Double> singleGapMap = new HashMap<>();
		Map<Double, Double> secondGapMap = new HashMap<>();
		Map<Double, Double> tripleGapMap = new HashMap<>();
		// 保留一位小数
		DecimalFormat decimalFormat = new DecimalFormat("#.0");
		double yt_T = lastSecondPredictParam;
		double yt3_T = lastTriplePredictParam;

		// （2）平滑值区间 [1~10]/10,先做一次平滑
		for (int i = 0; i < realParamList.size(); i++) {
//			System.out.println("实际值是："
//					+ realParamList.get(i) + ",一次平滑预测值是："
//					+ lastSinglePredictParam + "，误差为："
//					+ decimalFormat.format(Math.abs(realParamList.get(i) - lastSinglePredictParam))
//					+ "；二次平滑预测值是：" + yt_T + ",误差为："
//					+ decimalFormat.format(Math.abs(realParamList.get(i) - yt_T))
//					+ ";三次平滑预测值是：" + yt3_T + ",误差为："
//					+ decimalFormat.format(Math.abs(realParamList.get(i) - yt3_T)));
			// 统计全部的误差
			singleGapList.add(Math.abs(realParamList.get(i) - lastSinglePredictParam));
			secondGapList.add(Math.abs(realParamList.get(i) - yt_T));
			tripleGapList.add(Math.abs(realParamList.get(i) - yt3_T));

			// （3）获得一次平滑预测值
			lastSinglePredictParam = smoothingFactor * realParamList.get(i) + (1 - smoothingFactor) * lastSinglePredictParam;
			// 保留一位小数
			lastSinglePredictParam = Double.valueOf(decimalFormat.format(lastSinglePredictParam));
//			System.out.println("一次平滑预测下次为：" + lastSinglePredictParam);
			// （4）计算二次平滑值
			lastSecondPredictParam = smoothingFactor * lastSinglePredictParam + (1 - smoothingFactor) * lastSecondPredictParam;
			double at = 2 * lastSinglePredictParam - lastSecondPredictParam;
			double bt = smoothingFactor / (1 - smoothingFactor) * (lastSinglePredictParam - lastSecondPredictParam);
			// （4-1）计算出二次平滑预测值
			yt_T = at + bt * predictTime;
//			System.out.println("二次平滑为：" + lastSecondPredictParam + ",下" + predictTime + "期的预测值为：" + yt_T);

			// （5）计算三次平滑值
			lastTriplePredictParam = smoothingFactor * lastSecondPredictParam + (1 - smoothingFactor) * lastTriplePredictParam;
			double at3 = 3 * lastSinglePredictParam - 3 * lastSecondPredictParam + lastTriplePredictParam;
			// (5-1)根据时间顺序分配权重
			double bt_s0 = 2 * (1 - smoothingFactor) * (1 - smoothingFactor);
			double bt_s1 = smoothingFactor / bt_s0;
			double bt_s2 = (6 - 5 * smoothingFactor) * lastSinglePredictParam;
			double bt_s3 = 2 * (5 - 4 * smoothingFactor) * lastSecondPredictParam;
			double bt_s4 = (4 - 3 * smoothingFactor) * lastTriplePredictParam;
			double bt_s5 = bt_s1 * (bt_s2 - bt_s3 + bt_s4);
			double bt3 = bt_s5;
			double ct_0 = smoothingFactor * smoothingFactor / bt_s0;
			double ct_1 = 2 * lastSecondPredictParam;
			double ct_2 = lastSinglePredictParam - ct_1 + lastTriplePredictParam;
			double ct_3 = ct_0 * ct_2;
			// (5-2)计算三次滑动的预期值
			yt3_T = at3 + bt3 * predictTime + ct_3 * predictTime * predictTime;
			if ((i + 1) == realParamList.size()) {
//				System.out.println("三次平滑为：" + lastTriplePredictParam + ",下" + predictTime + "期的预测值为：" + yt3_T);
				log.info("预测值：{}", yt3_T);
				return yt3_T;

			}
		}

		// 计算MES均方误差
		double totalSingleGap = 0.0;
		double totalSecondGap = 0.0;
		double totalTripleGap = 0.0;
		for (Double singleGap : singleGapList) {
			totalSingleGap = totalSingleGap + singleGap * singleGap;
		}
		for (Double secondGap : secondGapList) {
			totalSecondGap = totalSecondGap + secondGap * secondGap;
		}
		for (Double tripleGap : tripleGapList) {
			totalTripleGap = totalTripleGap + tripleGap * tripleGap;
		}
		singleGapMap.put(smoothingFactor, Math.sqrt(totalSingleGap) / totalTimes);
		secondGapMap.put(smoothingFactor, Math.sqrt(totalSecondGap) / totalTimes);
		tripleGapMap.put(smoothingFactor, Math.sqrt(totalTripleGap) / totalTimes);
		// 每更换一个平滑值，预估值都要复位
		lastSinglePredictParam = tripleSmoothingEntity.getLastSinglePredictParam();
		lastSecondPredictParam = tripleSmoothingEntity.getLastSecondPredictParam();
		lastTriplePredictParam = tripleSmoothingEntity.getLastTriplePredictParam();
		yt_T = resizeSecondPredict;
		yt3_T = resizeTriplePredict;
		// 清空当前list装的误差值
		singleGapList.clear();
		secondGapList.clear();
		tripleGapList.clear();
		return 0.0;
	}

}